package com.kingmed.kmss.common.advice.exception;


import cn.dev33.satoken.exception.NotLoginException;
import cn.dev33.satoken.exception.NotPermissionException;
import cn.dev33.satoken.exception.NotRoleException;
import com.kingmed.kmss.common.enums.base.code.ApiResultCode;
import com.kingmed.kmss.common.enums.base.code.ResultCode;
import com.kingmed.kmss.common.exception.*;
import com.kingmed.kmss.common.exception.api.ApiException;
import com.kingmed.kmss.common.exception.bpmn.ProcessNoAuditUserException;
import com.kingmed.kmss.common.exception.bpmn.ProcessSubmittedException;
import com.kingmed.kmss.common.exception.external.ExternalApiException;
import com.kingmed.kmss.common.vo.CommonResponse;
import com.kingmed.kmss.common.vo.CommonResponseHelper;
import com.kingmed.kmss.common.vo.api.ApiCommonResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.ArrayList;
import java.util.List;

/**
 * CRM系统内部的异常处理
 */
@Slf4j
@RestControllerAdvice(basePackages = {"com.kingmed"})
public class CommonExceptionAdvice {

    /**
     * 通用异常-查询不到记录
     */
    @ExceptionHandler(value = NoRecordException.class)
    public CommonResponse<Object> handlerNoRecordException(NoRecordException e) {
        return this.commonResp(e, ResultCode.NO_RECORD_EXCEPTION);
    }

    /**
     * 通用异常-消息异常
     */
    @ExceptionHandler(value = MessageException.class)
    public CommonResponse<Object> handlerMessageException(MessageException e) {
        return this.commonResp(e, ResultCode.COMMON_BUSINESS_EXCEPTION);
    }

    /**
     * 业务异常（业务判断等出现的异常，跟BusinessException可以通用）
     */
    @ExceptionHandler(value = BusinessException.class)
    public CommonResponse<Object> handlerBusinessException(BusinessException e) {
        return this.commonResp(e, ResultCode.COMMON_BUSINESS_EXCEPTION);
    }

    @ExceptionHandler(value = IllegalStateException.class)
    public CommonResponse<Object> handlerIllegalStateException(IllegalStateException e) {
        return this.commonResp(e, ResultCode.COMMON_BUSINESS_EXCEPTION);
    }

    @ExceptionHandler(value = ProcessSubmittedException.class)
    public CommonResponse<Object> handlerProcessSubmittedException(ProcessSubmittedException e) {
        return this.commonResp(e, ResultCode.PROCESS_SUBMITTED_EXCEPTION);
    }


    @ExceptionHandler(value = ProcessNoAuditUserException.class)
    public CommonResponse<Object> handlerProcessNoAuditUserException(ProcessNoAuditUserException e) {
        return this.commonResp(e, ResultCode.PROCESS_NO_AUDIT_USER_EXCEPTION);
    }

    @ExceptionHandler(value = ConvertorException.class)
    public CommonResponse<Object> handlerConvertorException(ConvertorException e) {
        return this.commonResp(e, ResultCode.COMMON_BUSINESS_EXCEPTION);
    }

    /**
     * CRM调用外部接口(如KMCS营销，实验室等)的异常处理
     */
    @ExceptionHandler(value = ExternalApiException.class)
    public CommonResponse<Object> handle(ExternalApiException e) {
        log.error("调用外部接口发生异常", e);
        return this.commonResp(e, ResultCode.EXTERNAL_API_EXCEPTION);
    }

    /**
     * 参数校验异常
     */
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResponse<Object> handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
        CommonResponse<Object> response = new CommonResponse<>();
        List<String> msglist = new ArrayList<>();
        for (FieldError error : exception.getBindingResult().getFieldErrors()) {
            msglist.add(error.getField() + " : " + error.getDefaultMessage());
        }
        response.setCode(ResultCode.VALIDATE_FAILED.getCode());
        response.setMessage(String.join(",", msglist));
        return response;
    }

    @ExceptionHandler(value = BindException.class)
    public CommonResponse<Object> handleValidException(BindException e) {
        BindingResult bindingResult = e.getBindingResult();
        String message = null;
        if (bindingResult.hasErrors()) {
            FieldError fieldError = bindingResult.getFieldError();
            if (fieldError != null) {
                message = fieldError.getField() + fieldError.getDefaultMessage();
            }
        }
        return CommonResponseHelper.validateFailed(message);
    }

    /**
     * 跟统一认证和授权相关的异常处理（依赖SA-TOKEN）
     */
    @ResponseBody
    @ExceptionHandler(value = NotLoginException.class)
    public CommonResponse<Object> handleNotLoginException(NotLoginException e) {
        return CommonResponseHelper.unauthorized(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = NotRoleException.class)
    public CommonResponse<Object> handleNotRoleException(NotRoleException e) {
        return CommonResponseHelper.forbidden(e.getMessage());
    }

    @ResponseBody
    @ExceptionHandler(value = NotPermissionException.class)
    public CommonResponse<Object> handleNotPermissionException(NotPermissionException e) {
        return CommonResponseHelper.forbidden(e.getMessage());
    }


    /**
     * CRM对外标准接口的异常处理
     */
    @ExceptionHandler(value = ApiException.class)
    public ApiCommonResponse<Object> handle(ApiException e) {
        log.error("对外标准接口发生异常", e);
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        return new ApiCommonResponse<>(ApiResultCode.FATAL_FAILED.getCode(),
                StringUtils.isNotBlank(e.getMessage()) ? e.getMessage() : ApiResultCode.FATAL_FAILED.getMessage());
    }

    /**
     * CRM内部跨服务调用的异常处理
     */
    @ExceptionHandler(value = InternalApiException.class)
    public CommonResponse<Object> handle(InternalApiException e) {
        log.error("内部服务调用发生异常", e);
        return this.commonResp(e, ResultCode.INTERNAL_API_EXCEPTION);
    }

    /**
     * 找不到对应异常类的情况下，统一进Exception
     */
    @ExceptionHandler(value = Exception.class)
    public CommonResponse<Object> handlerException(Exception e) throws Exception {
        log.error("发生未知异常", e);
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        return new CommonResponse<>(ResultCode.UNKNOWN_EXCEPTION.getCode(),
                ResultCode.UNKNOWN_EXCEPTION.getMessage());
    }

    /**
     * 执行响应
     */
    private CommonResponse<Object> commonResp(RuntimeException e, ResultCode resultCode) {
        log.warn("{}：{}", e.getClass().getName(), e.getMessage());
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }
        return new CommonResponse<>(resultCode.getCode(),
                StringUtils.isNotBlank(e.getMessage()) ? e.getMessage() : resultCode.getMessage());
    }
}
